﻿using System;

namespace Cpu6502.Opcodes
{
    /// <summary>
    /// SBC #AA - SUBs passed value to Accumulator
    /// </summary>
    class SBC_I : Opcode
    {
        SBCUtility helper = null;

        public SBC_I()
        {
            helper = new SBCUtility();
            this.CpuCycles = 2;
            this.AddressType = CPU.AddressType.Immediate;
        }

        public override void Execute()
        {
            helper.Argument = base.Argument;
            helper.Execute();
        }
    }

    /// <summary>
    /// SBC $AA - SUB using ZeroPage Addressing
    /// </summary>
    class SBC_ZP : Opcode
    {
        SBCUtility helper = null;

        public SBC_ZP()
        {
            helper = new SBCUtility();
            this.CpuCycles = 3;
            this.AddressType = CPU.AddressType.ZeroPage;
        }

        public override void Execute()
        {
            helper.Argument =
                CPU.Memory
                [
                    MemoryHelper.GetAddress
                    (base.Argument,
                    CPU.AddressType.ZeroPage)
                ];

            helper.Execute();
        }
    }

    /// <summary>
    /// SBC $AA, X - SUB using ZeroPage X Addressing
    /// </summary>
    class SBC_ZP_X : Opcode
    {
        SBCUtility helper = null;

        public SBC_ZP_X()
        {
            helper = new SBCUtility();
            this.CpuCycles = 4;
            this.AddressType = CPU.AddressType.ZeroPageIndexedX;
        }

        public override void Execute()
        {
            helper.Argument =
                CPU.Memory
                [
                    MemoryHelper.GetAddress
                    (base.Argument,
                    CPU.AddressType.ZeroPageIndexedX)
                ];

            helper.Execute();
        }
    }

    /// <summary>
    /// SBC $AAAA - SUB using Absolute Addressing
    /// </summary>
    class SBC_A : Opcode
    {
        SBCUtility helper = null;

        public SBC_A()
        {
            helper = new SBCUtility();
            this.CpuCycles = 4;
            this.AddressType = CPU.AddressType.Absolute;
        }

        public override void Execute()
        {
            helper.Argument =
                CPU.Memory
                [
                    MemoryHelper.GetAddress
                    (base.Argument,
                    CPU.AddressType.Absolute)
                ];

            helper.Execute();
        }
    }

    /// <summary>
    /// SBC $AAAA,X - SUB using Absolute X Addressing
    /// </summary>
    class SBC_A_X : Opcode
    {
        SBCUtility helper = null;

        public SBC_A_X()
        {
            helper = new SBCUtility();
            this.CpuCycles = 4;
            this.AddressType = CPU.AddressType.AbsoluteIndexedX;
        }

        public override void Execute()
        {
            helper.Argument =
                CPU.Memory
                [
                    MemoryHelper.GetAddress
                    (base.Argument,
                    CPU.AddressType.AbsoluteIndexedX)
                ];

            helper.Execute();
        }
    }

    /// <summary>
    /// SBC $AAAA,Y - SUB using Absolute Y Addressing
    /// </summary>
    class SBC_A_Y : Opcode
    {
        SBCUtility helper = null;

        public SBC_A_Y()
        {
            helper = new SBCUtility();
            this.CpuCycles = 4;
            this.AddressType = CPU.AddressType.AbsoluteIndexedY;
        }

        public override void Execute()
        {
            helper.Argument =
                CPU.Memory
                [
                    MemoryHelper.GetAddress
                    (base.Argument,
                    CPU.AddressType.AbsoluteIndexedY)
                ];

            helper.Execute();
        }
    }

    /// <summary>
    /// SBC ($AA),Y - SUB using Indirect_Index Addressing
    /// </summary>
    class SBC_Indirect_Index : Opcode
    {
        SBCUtility helper = null;

        public SBC_Indirect_Index()
        {
            helper = new SBCUtility();
            this.CpuCycles = 5;
            this.AddressType = CPU.AddressType.IndirectIndexed;
        }

        public override void Execute()
        {
            helper.Argument =
                CPU.Memory
                [
                    MemoryHelper.GetAddress
                    (base.Argument,
                    CPU.AddressType.IndirectIndexed)
                ];

            helper.Execute();
        }
    }

    /// <summary>
    /// SBC ($AA,X) - SUB using Index Indirect Addressing
    /// </summary>
    class SBC_Index_Indirect : Opcode
    {
        SBCUtility helper = null;

        public SBC_Index_Indirect()
        {
            helper = new SBCUtility();
            this.CpuCycles = 6;
            this.AddressType = CPU.AddressType.IndexedIndirect;
        }

        public override void Execute()
        {
            helper.Argument =
                CPU.Memory
                [
                    MemoryHelper.GetAddress
                    (base.Argument,
                    CPU.AddressType.IndexedIndirect)
                ];

            helper.Execute();
        }
    }

    /// <summary>
    /// Does the actual SDC operation
    /// </summary>
    class SBCUtility
    {
        public Int16 Argument;

        public void Execute()
        {
            Int16 data = (Int16)(CPU.Registers.A - this.Argument - (CPU.Registers.P.C ? 0 : 1));
            
            // Set Sign Flag
            CPU.Registers.P.N = (data < 0);

            // Set Zero Flag
            CPU.Registers.P.Z = (data == 0);

            // Set Overflow Flag
            CPU.Registers.P.V = (((CPU.Registers.A ^ data) & 0x80) != 0) 
                && (((CPU.Registers.A ^ this.Argument) & 0x80) != 0);
            
            // If Decimal
            if (CPU.Registers.P.D)
            {
                if ((((CPU.Registers.A & 0xf) - (CPU.Registers.P.C ? 0 : 1))
                    < (this.Argument & 0xf)))
                {
                    data -= 6;
                }

                if (data > 0x99)
                    data -= 0x60;
            }

            // Set Carry Flag
            CPU.Registers.P.C = (data < 0x100);

            // Set value back into accumulator
            CPU.Registers.A = (byte)(data & 0xff);
        }
    }
}
